<?php

namespace Syrius\Type;

/**
 * Enter description here ...
 * @abstract
 * @package	Type
 * @author	Domenico Biancardi <domenico.biancardi@gmail.com>
 * @link	http://code.google.com/p/syrius/
 *
 */
abstract class Relation extends Type {
	/**
	 * Generazione della rappresentazione sql
	 */
	public function __toString() {

		$str = $this->type . "(" . $this->length . ")";
		// column definition
		if ($this->serial) {
			$str .= " AUTO_INCREMENT";
		}
		if ($this->null) {
			$str .= " NULL";
		} else {
			$str .= " NOT NULL";
		}
		if ($this->default) {
			$str .= " DEFAULT " . $this->default;
		}
		if ($this->comment) {
			$str .= " COMMENT '" . $this->comment . "'";
		}
		if ($this->column_format) {
			$str .= " COLUMN_FORMAT " . $this->column_format;
		}
		if ($this->storage) {
			$str .= " STORAGE " . $this->storage;
		}

		// index
		if ($this->primary) {
		}
		if ($this->index) {
		}
		if ($this->unique) {
		}
		if ($this->fulltext) {
		}

		if ($this->where != "") {
			$key = array_keys($this->where);
			$val = array_values($this->where);
	 		$val = str_replace("entity.", "", $val [0]);
			$foreignKey = new \Syrius\Keys\ForeignKey ($val);
			// cerco la chiave primaria della classe mapper
			$tmp = new $this->mapper ();
			$tmpField = $tmp->getPrimaryKey();
			$foreignKey->addCondition($this->_source, $this->mapper, $tmpField->getName());
			return $str . ", " . $foreignKey;
		}
		return $str;
	}

	/**
	 * Where conditions che determiina il join
	 */
	protected $where;

    /**
     * Nel caso di una relazione il controllo deve essere coerente
     * con il tipo di campo che si ha a che fare
     * @return string
     */
    public function _toDb($input){
        $opts["type"] = $this -> type;
        $className = '\Syrius\Type\\' . ucfirst($this -> type);
        $field = new $className($opts);
        return $field -> _toDb($input);
    }
	public function getMapper() {
		return $this->mapper;
	}

	/**
	 * Classe della relazione
	 */
	protected $mapper;

	protected function typeControl($input) {
		// Bypass
	}

	/**
	 * Funzione che permette di togliere tutte le chiavi da un field, la funzione viene utilizzata quando si effettua un update table
	 */
	public function removeKeys() {
		$this->index = false;
		$this->where = "";
	}

}

class Relation_Many_To_Many extends Relation {
	/**
	 * Tabella nascota che viene generata per la gestione di una relazione Molti a Molti
	 */
	private $manyTable;

	public function __construct($opts, $fieldName = "", $tableName = "", $currentKey = "id") {
		parent::__construct($opts);
		$this->fieldName = $fieldName;
		$this->tableName = $tableName;
		if (!isset ($opts ["where"])) {
			// search a primary key of mapper relation
			$mapperName = $opts ["mapper"];
			$mapper = new $mapperName ();
			$this->mapperName = $mapper->source();

			$this->manyTable = $this->getManyTableName($tableName, $this->mapperName);
			$this->mmModel(); // carica dinamicamente il modello


			$mapperField = $mapper->fields();
			$mapperKey = "";
			foreach ($mapperField as $k => $v) {
				if ($v->getAttribute("primary") == true) {
					$mapperKey = $k;
				}
			}
			$this->where = array(array($this->manyTable . "." . $fieldName => $mapperName . "." . $mapperKey), array($this->manyTable . "." . $tableName . "_id" => $tableName . "." . $currentKey));
		}
	}

	/**
	 * Crea in linea il modello di connessione tra le due classi, questa funzione effettua anche il migrate per creare la tabella di relazione molti a moltii
	 */
	private function mmModel() {
		$str = 'class ' . $this->manyTable . ' extends \\Syrius\\Model\\Model{
        	        	protected $_table = "' . $this->manyTable . '";
        	        	protected $id = array ("type" => "int",
        	        	   	"primary" => true,
        	        	   	"serial" => true,
        	        	   	"comment" => "Automatica Relazione Molti a Molti");
        	        	protected $' . $this->tableName . '_id = array("type" => "relation",
        	        	    "extra_type" => "one",
        	        	    "mapper" => "' . $this->tableName . '");
        	        	protected $' . $this->mapperName . '_id = array("type" => "relation",
        	        	    "extra_type" => "one",
        	        	    "mapper" => "' . $this->mapperName . '");
        	        }';
		if (!in_array($this->manyTable, get_declared_classes())) {
			eval ($str);
			$baseModel = new $this->tableName ();
			$baseModel->migrate();
			$tmp = new $this->manyTable ();
			$tmp->migrate();
		}
	}

	private $fieldName;

	private $tableName;

	private $mapperName;

	/**
	 * Questa funzione cerca di capire se la tabella di relazione tra le due entità è già esistente e ritorna qual'è il nome corretto seguendo la procedura:
	 * 1 - mm_b_a esiste? no
	 * 2 - mm_a_b viene creata
	 * ____
	 * 1 - mm_b_a esiste? si
	 * 2 - la tabella corretta è mm_b_a
	 */
	private function getManyTableName($entityA, $entityB) {
		$tableTest = "mm_" . $entityB . "_" . $entityA;
		$stat = new \Syrius\Adapter\Stat ();
		try {
			$stat->tableExists($tableTest);
			return "mm_" . $entityB . "_" . $entityA;
		} catch (Syrius\Exception\Table_Not_Exists $e) {
			return "mm_" . $entityB . "_" . $entityA;
		}

	}

	/**
	 * Generazione della rappresentazione sql
	 */
	public function __toString() {
		//  nel caso di una relazione il tipo è un intero
		$this->type = "INT";

		$str = $this->type . "(" . $this->length . ")";
		// column definition
		if ($this->serial) {
			$str .= " AUTO_INCREMENT";
		}
		if ($this->null) {
			$str .= " NULL";
		} else {
			$str .= " NOT NULL";
		}
		if ($this->default) {
			$str .= " DEFAULT " . $this->default;
		}
		if ($this->comment) {
			$str .= " COMMENT '" . $this->comment . "'";
		}
		if ($this->column_format) {
			$str .= " COLUMN_FORMAT " . $this->column_format;
		}
		if ($this->storage) {
			$str .= " STORAGE " . $this->storage;
		}

		// index
		if ($this->primary) {
		}
		if ($this->index) {
		}
		if ($this->unique) {
		}
		if ($this->fulltext) {
		}

		$key = array_keys($this->where [0]);
		$val = array_values($this->where [0]);
		$key = str_replace($this->manyTable . ".", "", $key [0]);
		$foreignKey = new \Syrius\Keys\ForeignKey ($key);
		$foreignKey->addCondition($this->_source, $this->mapper, $key);

		//return $str . ", " .  $foreignKey;
		return "";
	}

	/**
	 * Funzione che effettua il salvataggio sulla tabella many_to_many
	 * @TODO nel caso di un update migliorare la procedura che effettua il delete prima dell'insert
	 */
	public function save($insertId, $data) {
		$modelTmp = new $this->manyTable ();
		$field = $this->tableName . "_id";
		$field2 = $this->mapperName . "_id";
		// cancello per $field = $insertId
		$delete = new \Syrius\Query\Delete ($this->manyTable);
		$delete->where(array($field => $insertId));
		$query = new \Syrius\Adapter\Query ($delete);
		$list = $data->data();
		for ($i = 0; $i < count($list); $i++) {
			$objTmp = $modelTmp->getEntity();
			// @todo perchè non ho l'oggetto??
			$objTmp->$field2 = $list [$i]["id"];
			$objTmp->$field = $insertId;
			$objTmp->save();
		}
	}

	public function getManyTable() {
		return $this->manyTable;
	}

	public function getTableName() {
		return $this->tableName;
	}

}

class Relation_One extends Relation {
	public function __construct($opts, $fieldName = "") {
		parent::__construct($opts);
		//  nel caso di una relazione il tipo è un intero
		$this->type = "INT";

		if (!isset ($opts ["where"])) {
			// search a primary key of mapper relation
			$mapperName = $opts ["mapper"];
			$mapper = new $mapperName ();
			$mapperField = $mapper->fields();
			$mapperPrimary = "";
			foreach ($mapperField as $k => $v) {
				if ($v->getAttribute("primary") == true) {
					$mapperPrimary = $k;
					$this->type = $v->getAttribute("type");
				}
			}
			$this->where = array($mapperPrimary => "entity." . $fieldName);
		}
	}

}

?>
